package mark.wxming.twotigerlock.lock.memory;

import mark.wxming.twotigerlock.lock.ILockHandler;
import mark.wxming.twotigerlock.lock.ILockIdentify;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 
 * 内存锁,适用于 单个JVM 
 * @Author douxy
 * @Date 2015年8月5日 下午12:03:37 
 */
public class MemoryLockHandler implements ILockHandler {
    private static final Logger LOGGER = Logger.getLogger(MemoryLockHandler.class);
    private static final ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();

    @Override
    public boolean tryLock(ILockIdentify iLockIdentify) {
        return tryLock(iLockIdentify, 0, null);
    }

    @Override
    public boolean tryLock(ILockIdentify iLockIdentify, long timeout, TimeUnit unit) {
        String key = (String) iLockIdentify.uniqueIdentify();
        try {
            long nano = System.nanoTime();
            do {
                LOGGER.debug("try lock key: " + key);
                if (locks.putIfAbsent(key, new Object()) == null)
                    return true;
                if (timeout == 0) {
                    break;
                }
                Thread.sleep(300);
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));
            return false;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return false;
    }

    @Override
    public void lock(ILockIdentify iLockIdentify) {
        String key = (String) iLockIdentify.uniqueIdentify();
        try {
            do {
                if (locks.putIfAbsent(key, new Object()) == null)
                    return;
                Thread.sleep(300);
                LOGGER.info("Locked by another business!waiting...");
            } while (true);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    @Override
    public void unLock(ILockIdentify iLockIdentify) {
        List<ILockIdentify> list = new ArrayList<ILockIdentify>();
        list.add(iLockIdentify);
        unLock(list);
    }

    @Override
    public boolean tryLock(List<ILockIdentify> iLockIdentifyList) {
        return tryLock(iLockIdentifyList, 0L, null);
    }

    @Override
    public boolean tryLock(List<ILockIdentify> iLockIdentifyList, long timeout, TimeUnit unit) {
        try {
            List<String> needLocking = new CopyOnWriteArrayList<String>();
            List<String> locked = new CopyOnWriteArrayList<String>();
            long nano = System.nanoTime();
            for (ILockIdentify identify : iLockIdentifyList) {
                String key = (String) identify.uniqueIdentify();
                needLocking.add(key);
            }
            do {
                for (String key : needLocking) {
                    if (locks.putIfAbsent(key, new Object()) == null)
                        locked.add(key);
                }
                needLocking.removeAll(locked); // 已锁定资源去除
                if (CollectionUtils.isEmpty(needLocking)) {
                    return true;
                } else {
                    // 部分资源未能锁住
                    LOGGER.debug("keys: " + needLocking + " locked by another business：");
                }

                if (timeout == 0) {
                    break;
                }
                Thread.sleep(500);
            } while ((System.nanoTime() - nano) < unit.toNanos(timeout));

            // 得不到锁，释放锁定的部分对象，并返回失败
            if (!CollectionUtils.isEmpty(locked)) {
                for (String lockedKey : locked) {
                    locks.put(lockedKey, null);
                }
            }
            return false;
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
        return true;
    }

    @Override
    public void unLock(List<ILockIdentify> iLockIdentifyList) {
        String key = null;
        for (ILockIdentify identify : iLockIdentifyList) {
            key = (String) identify.uniqueIdentify();
            locks.remove(key);
        }
    }
}
